#include "cf_includes.h"


void ChangeGamma(byte *pImage, int size, float factor)
{

  for(int i = 0; i < size / 3; i++, pImage += 3) 
  {
    float scale = 1.0f, temp = 0.0f;
    float r = 0, g = 0, b = 0;


    r = (float)pImage[0];
    g = (float)pImage[1];
    b = (float)pImage[2];


    r = r * factor / 255.0f;
    g = g * factor / 255.0f;
    b = b * factor / 255.0f;


    if(r > 1.0f && (temp = (1.0f/r)) < scale) scale=temp;
    if(g > 1.0f && (temp = (1.0f/g)) < scale) scale=temp;
    if(b > 1.0f && (temp = (1.0f/b)) < scale) scale=temp;


    scale*=255.0f;		
    r*=scale;	g*=scale;	b*=scale;


    pImage[0] = (byte)r;
    pImage[1] = (byte)g;
    pImage[2] = (byte)b;
  }
}

CCF_Bsp::CCF_Bsp()
{
  m_pVB = NULL;
  m_pIB = NULL;
  m_pTextureMaps = NULL;
  m_pLightMaps = NULL;

  m_nVerticesCount = 0;
  m_nFacesCount = 0;
  m_nIndicesCount = 0;
  m_nTexturesCount = 0;
  m_nLightmapCount = 0;
  m_nPlanesCount = 0;
  m_nNodesCount = 0;
  m_nLeavesCount = 0;
  m_nLeafFacesCount = 0;
  m_nBrushesCount = 0;
  m_nBrushSidesCount = 0;
  m_nLeafBrushesCount = 0;

  ZeroMemory(&m_sVisData, sizeof(BSP_VISDATA));

  m_pTextures = NULL;
  m_pLightmap = NULL;
  m_pVertices = NULL;
  m_pFaces = NULL;
  m_pIndices = NULL;
  m_pBrushes = NULL;
  m_pBrushSides = NULL;
  m_pLeafBrushes = NULL;

  m_pRenderState[ rShowTextureMap ] = true;
  m_pRenderState[ rShowLightMap ] = true;
  m_pRenderState[ rUsePVS ] = true;
}

CCF_Bsp::~CCF_Bsp()
{
  m_pIB->Release();
  m_pVB->Release();

  delete [] m_pVertices;
  delete [] m_pIndices;
  delete [] m_pFaces;
  delete [] m_pTextures;
}

BOOL CCF_Bsp::Initialize(CCF_DrawRenderer* renderer, const LPWSTR filename )
{
  m_renderer = renderer;

  if((m_pDevice = renderer->GetDevice()) == NULL)
    return false;

  return Load( filename );
}

BOOL CCF_Bsp::Load(const LPWSTR strFileName)
{
  HANDLE fhnd = CreateFile( strFileName, GENERIC_READ, FILE_SHARE_READ, 
    NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

  if( fhnd != 0 )
  {
    DWORD readSize;
    BSP_HEADER header;
    ReadFile( fhnd, &header, sizeof(BSP_HEADER), &readSize, NULL );

    m_nVerticesCount = header.entries[ lVertcies ].length / sizeof(BSP_VERTEX);
    m_pVertices = new BSP_VERTEX[m_nVerticesCount];

    m_nFacesCount = header.entries[lFaces].length / sizeof(BSP_FACE);
    m_pFaces = new BSP_FACE[m_nFacesCount];

    m_nIndicesCount = header.entries[lIndices].length / sizeof(BSP_INDEX);
    m_pIndices = new BSP_INDEX[m_nIndicesCount];

    m_nTexturesCount = header.entries[lTextures].length / sizeof(BSP_TEXTURE);
    m_pTextureMaps = new LPDIRECT3DTEXTURE9 [m_nTexturesCount];
    m_pTextures = new BSP_TEXTURE [m_nTexturesCount];

    m_nLightmapCount = header.entries[lLightmaps].length / sizeof(BSP_LIGHTMAP);
    m_pLightMaps = new LPDIRECT3DTEXTURE9 [m_nLightmapCount];
    m_pLightmap = new BSP_LIGHTMAP [m_nLightmapCount];

    m_nPlanesCount = header.entries[lPlanes].length / sizeof(BSP_PLANE);
    m_pPlanes = new BSP_PLANE[m_nPlanesCount]; 

    m_nNodesCount = header.entries[lNodes].length / sizeof(BSP_NODE);
    m_pNodes = new BSP_NODE[m_nNodesCount];

    m_nLeavesCount = header.entries[lLeafs].length / sizeof(BSP_LEAF);
    m_pLeaves = new BSP_LEAF[m_nLeavesCount];

    m_nLeafFacesCount = header.entries[lLeaffaces].length / sizeof(BSP_LEAFFACES);
    m_pLeafFaces = new BSP_LEAFFACES[m_nLeafFacesCount];

    m_nBrushesCount = header.entries[lBrushes].length / sizeof(BSP_BRUSH);
    m_pBrushes = new BSP_BRUSH[ m_nBrushesCount ];

    m_nBrushSidesCount = header.entries[lBrushSides].length / sizeof(BSP_BRUSHSIDES);
    m_pBrushSides = new BSP_BRUSHSIDES[ m_nBrushSidesCount ];

    m_nLeafBrushesCount = header.entries[lLeafbrushes].length / sizeof(BSP_LEAFBRUSHES);
    m_pLeafBrushes = new BSP_LEAFBRUSHES[ m_nLeafBrushesCount ];

    SetFilePointer( fhnd, header.entries[lVertcies].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pVertices, m_nVerticesCount*sizeof(BSP_VERTEX), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lIndices].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pIndices, m_nIndicesCount*sizeof(BSP_INDEX), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lFaces].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pFaces, m_nFacesCount*sizeof(BSP_FACE), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lTextures].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pTextures, m_nTexturesCount*sizeof(BSP_TEXTURE), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lLightmaps].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pLightmap, m_nLightmapCount*sizeof(BSP_LIGHTMAP), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lPlanes].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pPlanes, m_nPlanesCount*sizeof(BSP_PLANE), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lNodes].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pNodes, m_nNodesCount*sizeof(BSP_NODE), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lLeafs].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pLeaves, m_nLeavesCount*sizeof(BSP_LEAF), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lLeaffaces].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pLeafFaces, m_nLeafFacesCount*sizeof(BSP_LEAFFACES), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lBrushes].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pBrushes, m_nBrushesCount*sizeof(BSP_BRUSH), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lBrushSides].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pBrushSides, m_nBrushSidesCount*sizeof(BSP_BRUSHSIDES), &readSize, NULL );

    SetFilePointer( fhnd, header.entries[lLeafbrushes].offset, NULL, FILE_BEGIN );
    ReadFile( fhnd, m_pLeafBrushes, m_nLeafBrushesCount*sizeof(BSP_LEAFBRUSHES), &readSize, NULL );

    if(header.entries[lVisdata].length) 
    {
      SetFilePointer( fhnd, header.entries[lVisdata].offset, NULL, FILE_BEGIN );
      ReadFile( fhnd, &(m_sVisData.n_vecs),sizeof(int), &readSize, NULL );
      ReadFile( fhnd, &(m_sVisData.vecSize),sizeof(int), &readSize, NULL );

      int size = (m_sVisData.n_vecs * m_sVisData.vecSize);
      m_sVisData.vecs = new byte[size];

      ReadFile( fhnd, m_sVisData.vecs, sizeof(byte) * size, &readSize, NULL );
    }
    else
    {
      m_sVisData.vecs = NULL;
    }

    CloseHandle( fhnd );

    Build();

    return true;
  }

  return false;
}

INT CCF_Bsp::FindLeaf(D3DXVECTOR3 camPos)
{
  int index = 0;

  while (index >= 0) 
  {
    const BSP_NODE&  node  = m_pNodes[index];
    const BSP_PLANE& plane = m_pPlanes[node.plane];
    const double distance = D3DXVec3Dot( &plane.normal, &camPos ) - plane.dist; 

    if (distance >= 0) 
    {
      index = node.children[0];
    } 
    else 
    {
      index = node.children[1];
    }
  }

  return ~index;
}

BOOL CCF_Bsp::ClusterVisible(int visCluster, int testCluster) {

  if ((m_sVisData.vecs == NULL) || (visCluster < 0)) 
  {
    return true;
  }

  byte visSet = m_sVisData.vecs[(visCluster * m_sVisData.vecSize) + (testCluster >> 3)];

  return (visSet & (1 << (testCluster & 7))) != 0;
}

void CCF_Bsp::Build()
{	
  WCHAR filename[ MAX_PATH ];

  for(int i = 0; i < m_nTexturesCount; i++)
  {
    MultiByteToWideChar( CP_ACP, NULL, m_pTextures[i].name, 64, filename, MAX_PATH );
    m_pTextureMaps[i] = m_renderer->GetTextureLoader()->LoadFromFile( filename );
  }

  D3DLOCKED_RECT lockedRect;
  IDirect3DSurface9 *pLightmapSurface;
  unsigned char *pSurface;
  DWORD dwCharsPerRow;

  for (int i = 0; i < m_nLightmapCount; i++)
  {
    ChangeGamma((unsigned char *)m_pLightmap[i].imageBits, 128*128*3, 7);
    D3DXCreateTexture(m_pDevice, 128, 128, D3DX_DEFAULT, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &m_pLightMaps[i]);

    m_pLightMaps[i]->GetSurfaceLevel(0, &pLightmapSurface);
    pLightmapSurface->LockRect(&lockedRect, NULL, 0);

    pSurface = static_cast<unsigned char*>(lockedRect.pBits);
    dwCharsPerRow = lockedRect.Pitch;


    for (DWORD r = 0; r < 128; r++)
    {
      DWORD dwIndex = r * dwCharsPerRow;
      for (DWORD c = 0; c < 128; c++)
      {
        pSurface[dwIndex + c*4 + 0] = m_pLightmap[i].imageBits[r][c][2];
        pSurface[dwIndex + c*4 + 1] = m_pLightmap[i].imageBits[r][c][1];
        pSurface[dwIndex + c*4 + 2] = m_pLightmap[i].imageBits[r][c][0];
        pSurface[dwIndex + c*4 + 3] = 255;
      }
    }

    pLightmapSurface->UnlockRect();

    D3DXFilterTexture(m_pLightMaps[i], 0, 0, D3DX_FILTER_LINEAR);
  }

  m_pDevice->CreateVertexBuffer( m_nVerticesCount * sizeof(BSP_CUSTOM_VERTEX), 0,
    BSP_CUSTOM_VERTEX_FVF, D3DPOOL_DEFAULT, &m_pVB, NULL );

  BSP_CUSTOM_VERTEX *pVertices = NULL;

  m_pVB->Lock(0, m_nVerticesCount * sizeof(BSP_CUSTOM_VERTEX), (VOID**) &pVertices, 0);

  for(int i = 0; i < m_nVerticesCount; i++)
  {
    pVertices[i].x = m_pVertices[i].position[0];
    pVertices[i].y = m_pVertices[i].position[1];
    pVertices[i].z = m_pVertices[i].position[2];

    pVertices[i].nx = m_pVertices[i].normal[0];
    pVertices[i].ny = m_pVertices[i].normal[1];
    pVertices[i].nz = m_pVertices[i].normal[2];

    pVertices[i].color = D3DCOLOR_RGBA(255,255,255,255);

    pVertices[i].tu = m_pVertices[i].texcoord[0];
    pVertices[i].tv = m_pVertices[i].texcoord[1];

    pVertices[i].lu = m_pVertices[i].lightmap[0];
    pVertices[i].lv = m_pVertices[i].lightmap[1];
  }

  m_pVB->Unlock();

  m_pDevice->CreateIndexBuffer( m_nIndicesCount * sizeof(BSP_INDEX), 0,
    D3DFMT_INDEX32, D3DPOOL_DEFAULT, &m_pIB, NULL );

  BSP_INDEX *pIndices = NULL;

  m_pIB->Lock(0, m_nIndicesCount * sizeof(BSP_INDEX), (VOID**) &pIndices, 0);

  memcpy(pIndices, m_pIndices, m_nIndicesCount * sizeof(BSP_INDEX));

  m_pIB->Unlock();


  DrawnFaces.Resize(m_nFacesCount);
}

void CCF_Bsp::Render(CCF_Camera* Camera)
{
  m_pDevice->SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,0);
  m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
  m_pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
  m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);

  m_pDevice->SetTextureStageState(1,D3DTSS_TEXCOORDINDEX,1);
  m_pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
  m_pDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  m_pDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
  m_pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);

  m_pDevice->SetFVF(BSP_CUSTOM_VERTEX_FVF);
  m_pDevice->SetStreamSource(0, m_pVB, 0, sizeof(BSP_CUSTOM_VERTEX));
  m_pDevice->SetIndices(m_pIB);

  m_pDevice->SetTexture(0, NULL);
  m_pDevice->SetTexture(1, NULL);

  if(m_pRenderState[rUsePVS])
  {
    DrawnFaces.ClearAll();

    int Leaf = FindLeaf(Camera->Position);
    int Cluster = m_pLeaves[Leaf].cluster;

    for(int l = 0; l < m_nLeavesCount; l++)
    {	
      if(ClusterVisible(Cluster, m_pLeaves[l].cluster))
      {
        for(int f = 0; f < m_pLeaves[l].n_leaffaces; f++)
        {
          if(!DrawnFaces.On(m_pLeafFaces[m_pLeaves[l].leafface + f]))
          {
            DrawnFaces.Set(m_pLeafFaces[m_pLeaves[l].leafface + f]);
          }
        }
      }
    }
  }
  else
  {
    DrawnFaces.SetAll();
  }

  int lastLightmap = -1;
  int lastTexture = -1;

  for(int i = 0; i < m_nFacesCount; i++)
  {
    int face = i;

    if((m_pFaces[face].type == 1 || m_pFaces[face].type == 3) && DrawnFaces.On(i))
    {
      if(m_pRenderState[rShowTextureMap])
      {
        if(m_pFaces[face].texture != lastTexture)
        {
          lastTexture = m_pFaces[face].texture;
          m_pDevice->SetTexture(0, m_pTextureMaps[lastTexture]);
        }
      }

      if(m_pRenderState[rShowLightMap])
      {
        if(m_pFaces[face].lm_index != lastLightmap)
        {
          lastLightmap = m_pFaces[face].lm_index;

          if(m_pFaces[face].lm_index < 0)
          {
            m_pDevice->SetTexture(1, NULL);
          }
          else
          {
            m_pDevice->SetTexture(1, m_pLightMaps[m_pFaces[face].lm_index]);
          }
        }
      }

      m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST , m_pFaces[face].vertex, 0, 
        m_pFaces[face].n_verts, m_pFaces[face].meshvert, (m_pFaces[face].n_meshverts/3));
    }
  }
}


void CCF_Bsp::SetRenderState(BSP_RENDER_STATES state, BOOL value)
{
  m_pRenderState[state] = value;
}

BOOL CCF_Bsp::GetRenderState(BSP_RENDER_STATES state)
{
  return m_pRenderState[state];
}


/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *

/////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
/////
/////	This checks a bunch of different heights to see if we can step up
/////
/////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*

void CCF_Bsp::TryToStep(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo)
{
  for(float height = 1.0f; (height <= 10)&&(!collInfo->IsCollision); height++)
  {
    collInfo->IsCollision = false;
    collInfo->TryStep = false;

    D3DXVECTOR3 vStepStart(vStart.x, vStart.y + height, vStart.z);
    D3DXVECTOR3 vStepEnd(vEnd.x, vStart.y + height, vEnd.z);

    Trace(vStepStart, vStepEnd, collInfo);
  }
}
void CCF_Bsp::Trace(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo)
{
  collInfo->TraceRatio = 1.0f;

  CheckNode(0, 0.0f, 1.0f, vStart, vEnd, collInfo);

  if(collInfo->TraceRatio == 1.0f)
  {
    collInfo->Position = vEnd;
  }
  else
  {
    D3DXVECTOR3 vNewPosition = vStart + collInfo->TraceRatio * (vEnd - vStart);
    D3DXVECTOR3 vMove = vEnd - vNewPosition;

    float distance = D3DXVec3Dot(&vMove, &collInfo->Normal);

    D3DXVECTOR3 vEndPosition = vEnd - collInfo->Normal*distance;

    Trace(vNewPosition, vEndPosition, collInfo);

    if(collInfo->Normal.y > 0.2f || collInfo->IsGround)
      collInfo->IsGround = true;
    else
      collInfo->IsGround = false;
  }
}

void CCF_Bsp::TraceRay(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo)
{
  collInfo->TraceType = ECF_TraceType::Ray;
  Trace(vStart, vEnd, collInfo);
}

void CCF_Bsp::TraceSphere(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, float radius, SCF_CollisionInfo* collInfo)
{
  collInfo->TraceType = ECF_TraceType::Shpeare;
  collInfo->IsCollision = false;
  collInfo->IsGround = false;
  collInfo->TraceRadius = radius;

  Trace(vStart, vEnd, collInfo);

  if(collInfo->IsCollision && collInfo->TryStep)
  {
    TryToStep(collInfo->Position, vEnd, collInfo);
  }
}

void CCF_Bsp::TraceBox(D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, D3DXVECTOR3 vMin, D3DXVECTOR3 vMax, SCF_CollisionInfo* collInfo)
{
  collInfo->TraceType = ECF_TraceType::Box;
  collInfo->TraceMaxs = vMax;
  collInfo->TraceMins = vMin;
  collInfo->IsCollision = false;
  collInfo->TryStep = false;
  collInfo->IsGround = false;
  collInfo->Extents = D3DXVECTOR3( -vMin.x > vMax.x ? -vMin.x : vMax.x, 
    -vMin.y > vMax.y ? -vMin.y : vMax.y, -vMin.z > vMax.z ? -vMin.z : vMax.z);
  
  Trace(vStart, vEnd, collInfo);

  if(collInfo->IsCollision && collInfo->TryStep)
  {
    TryToStep(collInfo->Position, vEnd, collInfo);
  }
}

void CCF_Bsp::CheckNode(int nodeIndex, float startRatio, float endRatio, D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo* collInfo)
{
  if(nodeIndex < 0)
  {
    BSP_LEAF *pLeaf = &m_pLeaves[-(nodeIndex + 1)];

    for(int i = 0; i < pLeaf->n_leafbrushes; i++)
    {
      BSP_BRUSH *pBrush = &m_pBrushes[m_pLeafBrushes[pLeaf->leafbrush + i]];

      if((pBrush->n_brushsides > 0) && (m_pTextures[pBrush->texture].contents & 1))
      {
        CheckBrush(pBrush, vStart, vEnd, collInfo);
      }
    }

    return;
  }

  BSP_NODE *pNode = &m_pNodes[nodeIndex];
  BSP_PLANE *pPlane = &m_pPlanes[pNode->plane];

  float startDistance = D3DXVec3Dot(&vStart, &pPlane->normal) - pPlane->dist;
  float endDistance = D3DXVec3Dot(&vEnd, &pPlane->normal) - pPlane->dist;
  float offset = 0.0f;

  if(collInfo->TraceType == ECF_TraceType::Shpeare)
  {
    offset = collInfo->TraceRadius;
  }
  else if(collInfo->TraceType == ECF_TraceType::Box)
  {	
    offset = (float)(fabs( collInfo->Extents.x * pPlane->normal.x ) +
      fabs( collInfo->Extents.y * pPlane->normal.y ) +
      fabs( collInfo->Extents.z * pPlane->normal.z ) );
  }

  if(startDistance >= offset && endDistance >= offset)
  {
    CheckNode(pNode->children[0], startDistance, endDistance, vStart, vEnd, collInfo);
  }
  else if(startDistance < -offset && endDistance < -offset)
  {
    CheckNode(pNode->children[1], startDistance, endDistance, vStart, vEnd, collInfo);
  }	
  else
  {
    float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
    D3DXVECTOR3 vMiddle;

    int side = pNode->children[0];

    if(startDistance < endDistance)
    {
      side = pNode->children[1];

      float inverseDistance = 1.0f / (startDistance - endDistance);
      Ratio1 = (startDistance - offset - BSP_FLOAT_EPSILON) * inverseDistance;
      Ratio2 = (startDistance + offset + BSP_FLOAT_EPSILON) * inverseDistance;
    }
    else if(startDistance > endDistance)
    {
      float inverseDistance = 1.0f / (startDistance - endDistance);
      Ratio1 = (startDistance + offset + BSP_FLOAT_EPSILON) * inverseDistance;
      Ratio2 = (startDistance - offset - BSP_FLOAT_EPSILON) * inverseDistance;
    }
    if (Ratio1 < 0.0f)
      Ratio1 = 0.0f;
    else if (Ratio1 > 1.0f) 
      Ratio1 = 1.0f;

    if (Ratio2 < 0.0f) 
      Ratio2 = 0.0f;
    else if (Ratio2 > 1.0f) 
      Ratio2 = 1.0f;

    middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
    vMiddle = vStart + ((vEnd - vStart) * Ratio1);

    CheckNode(side, startRatio, middleRatio, vStart, vMiddle, collInfo);

    middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
    vMiddle = vStart + ((vEnd - vStart) * Ratio2);

    if(side == pNode->children[1])
      CheckNode(pNode->children[0], middleRatio, endRatio, vMiddle, vEnd, collInfo);
    else
      CheckNode(pNode->children[1], middleRatio, endRatio, vMiddle, vEnd, collInfo);
  }
}

void CCF_Bsp::CheckBrush(BSP_BRUSH *pBrush, D3DXVECTOR3 vStart, D3DXVECTOR3 vEnd, SCF_CollisionInfo *collInfo )
{
  float startRatio = -1.0f;
  float endRatio = 1.0f;
  bool startsOut = false;

  for(int i = 0; i < pBrush->n_brushsides; i++)
  {
    BSP_BRUSHSIDES *pBrushSide = &m_pBrushSides[pBrush->brushside + i];
    BSP_PLANE *pPlane = &m_pPlanes[pBrushSide->plane];

    float offset = 0.0f;
    float startDistance = D3DXVec3Dot(&vStart, &pPlane->normal) - (pPlane->dist + offset);
    float endDistance = D3DXVec3Dot(&vEnd, &pPlane->normal) - (pPlane->dist + offset);

    D3DXVECTOR3 vOffset(0, 0, 0);

    if( collInfo->TraceType == ECF_TraceType::Shpeare )
    {
      offset = collInfo->TraceRadius;
    }
    else if(collInfo->TraceType == ECF_TraceType::Box)
    {
      vOffset.x = (pPlane->normal.x < 0)	? collInfo->TraceMaxs.x : collInfo->TraceMins.x;
      vOffset.y = (pPlane->normal.y < 0)	? collInfo->TraceMaxs.y : collInfo->TraceMins.y;
      vOffset.z = (pPlane->normal.z < 0)	? collInfo->TraceMaxs.z : collInfo->TraceMins.z;

      startDistance = D3DXVec3Dot(&(vStart + vOffset), &pPlane->normal) - pPlane->dist;
      endDistance = D3DXVec3Dot(&(vEnd + vOffset), &pPlane->normal) - pPlane->dist;
    }

    if(startDistance > 0)
      startsOut = true;

    if(startDistance > 0 && endDistance > 0)
      return;

    if(startDistance <= 0 && endDistance <= 0)
      continue;

    if(startDistance > endDistance)
    {
      float Ratio1 = (startDistance - BSP_FLOAT_EPSILON) / (startDistance - endDistance);

      if(Ratio1 > startRatio)
      {
        startRatio = Ratio1;
        collInfo->IsCollision = true;
        collInfo->Normal = pPlane->normal;

        if((vStart.x != vEnd.x || vStart.z != vEnd.z) && pPlane->normal.y != 1)
        {
          collInfo->TryStep = true;
        }

        if(collInfo->Normal.y >= 0.2f)
          collInfo->IsGround = true;
      }
    }
    else
    {
      float Ratio = (startDistance + BSP_FLOAT_EPSILON) / (startDistance - endDistance);

      if(Ratio < endRatio)
        endRatio = Ratio;
    }
  }

  if(startsOut == false)
  {
    return;
  }

  if(startRatio < endRatio)
  {
    if(startRatio > -1 && startRatio < collInfo->TraceRatio)
    {
      if(startRatio < 0)
        startRatio = 0;

      collInfo->TraceRatio = startRatio;
    }
  }
}
